﻿namespace HZY.Framework.Core.Quartz;

/// <summary>
/// 定时任务作业 特性标记
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ScheduledAttribute : TimeAttribute
{
    /// <summary>
    /// 任务名称
    /// </summary>
    public string? Name { get; set; }

    /// <summary>
    /// 分组名称
    /// </summary>
    public string? GroupName { get; set; }

    /// <summary>
    /// 定时表达式
    /// </summary>
    public string Cron { get; set; }

    /// <summary>
    /// 任务类型
    /// </summary>
    public QuartzJobTaskType JobTaskType { get; set; } = QuartzJobTaskType.Memory;

    /// <summary>
    /// 扩展对象 可以存放任意有效信息 用于自定义
    /// </summary>
    public Dictionary<string, object> Extensions { get; set; } = [];

    /// <summary>
    /// 执行超时时间（单位：s） 默认 ：30分钟
    /// </summary>
    public int ExecutionTimeout { get; set; } = (60 * 1) * 30;

    /// <summary>
    /// 定时任务作业
    /// <para>
    /// 任务分 2 种 一种是内存任务 一种是数据库任务
    /// </para>
    /// </summary>
    /// <param name="cron">定时表达式</param>
    public ScheduledAttribute(string cron) : base("")
    {
        this.Cron = cron;
    }

    /// <summary>
    /// 定时任务作业
    /// <para>
    /// 任务分 2 种 一种是内存任务 一种是数据库任务
    /// </para>
    /// </summary>
    /// <param name="cron">定时表达式</param>
    /// <param name="remark">备注</param>
    public ScheduledAttribute(string cron, string remark) : base(remark)
    {
        this.Cron = cron;
        this.Remark = remark;
    }

    /// <summary>
    /// 定时任务作业
    /// <para>
    /// 任务分 2 种 一种是内存任务 一种是数据库任务
    /// </para>
    /// </summary>
    /// <param name="jobTaskType">是否内存任务 true=内存任务 false=数据库任务</param>
    public ScheduledAttribute(QuartzJobTaskType jobTaskType) : base("")
    {
        this.Cron = "";
        this.JobTaskType = jobTaskType;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    /// <param name="timeContext"></param>
    /// <returns></returns>
    protected override string GetLogText(MethodContext context, TimeContext timeContext)
    {
        var logs = base.GetLogText(context, timeContext);

        logs += @$"
== 【任务表达式】
{Cron} 
== 【异常堆栈】
{context.Exception}
";
        return logs;
    }

    /// <summary>
    /// 方法执行异常
    /// </summary>
    /// <param name="context"></param>
    public override void OnException(MethodContext context)
    {
    }

    /// <summary>
    /// 方法执行成功后
    /// </summary>
    /// <param name="context"></param>
    public override void OnSuccess(MethodContext context)
    {
    }

    /// <summary>
    /// 扫描 任务计划标记 ScheduledAttribute
    /// </summary>
    /// <param name="assemblies"></param>
    /// <param name="jobTaskInfos"></param>
    /// <returns></returns>
    public static void ScanScheduledAttributes(List<Assembly>? assemblies, List<QuartzJobTaskInfo> jobTaskInfos)
    {
        if (assemblies is null) return;

        var typeList = new List<Type>();

        foreach (var item in assemblies)
        {
            try
            {
                // 必须是 class 并且 不能是 泛型类
                var classList = item.ExportedTypes
                        .Where(w => w.IsClass && !w.IsGenericType && w.IsPublic)
                        .ToList()
                    ;

                if (classList.Count == 0)
                {
                    continue;
                }

                typeList.AddRange(classList);
            }
            catch (Exception)
            {
                continue;
            }
        }

        ScanScheduledAttributes(typeList, jobTaskInfos);
    }

    /// <summary>
    /// 扫描 任务计划标记 ScheduledAttribute
    /// </summary>
    /// <param name="classTypeList"></param>
    /// <param name="jobTaskInfos"></param>
    public static void ScanScheduledAttributes(List<Type>? classTypeList, List<QuartzJobTaskInfo> jobTaskInfos)
    {
        if (classTypeList is null) return;

        foreach (var item in classTypeList)
        {
            if (item.IsGenericType || item.IsInterface) continue;

            //检测类型种是否有 ScheduledAttribute 特性
            var methodInfos = item.GetMethods();
            var methods = methodInfos
                .Where(w => w.GetCustomAttribute<ScheduledAttribute>() != null)
                .ToList();

            if (methods.Count == 0) continue;

            foreach (var methodInfo in methods)
            {
                var key = methodInfo.ReflectedType?.FullName + ">" + methodInfo.Name;

                if (jobTaskInfos.Any(w => w.Key == key))
                {
                    continue;
                }

                var jobTaskInfo = new QuartzJobTaskInfo();
                jobTaskInfos.Add(jobTaskInfo);
                jobTaskInfo.Key = key;
                jobTaskInfo.MethodInfo = methodInfo;
                jobTaskInfo.ScheduledAttribute = methodInfo.GetCustomAttribute<ScheduledAttribute>()!;
                jobTaskInfo.ClassType = item;
            }
        }
    }
}